In some specialised circumstances you may need to programatically select the cache used when a @Cacheable
or @CacheFlush
annotation is hit. An example might be a multi-tenant application where optimising cache utilisation by caching content for different tenants in their own caches would make sense.To apply cache selection you need a bean in the Spring context that implements the grails.plugin.springcache.CacheResolver
interface. The interface is extremely simple having only one method resolveCacheName(String)
which is passed the "base" cache name declared on your annotation and should return the actual cache name to use. You can then add a cacheResolver
parameter to @Cacheable
and @CacheFlush
annotations referencing the bean name of your CacheResolver
implementation.You can override the default CacheResolver
used by the plugin by simply redefining the bean springcacheDefaultCacheResolver in _resources.groovy_.Example usage
A simple example based on the Multi-Tenant plugin. Cached actions should use a different cache depending on the current tenant.First we define a CacheResolver
implementation that will simply append the current tenant id to the base cache name:import grails.plugin.springcache.CacheResolver
import org.springframework.web.context.request.RequestContextHolderclass MultiTenantCacheResolver implements CacheResolver { def tenantResolver // a component of the Multi-Tenant plugin, see that plugin's documentation String resolveCacheName(String baseName) {
def request = RequestContextHolder.requestAttributes.currentRequest
def tenantId = tenantResolver.getTenantFromRequest(request)
"${baseName}-tenant-${tenantId}"
}
}
Then we need to wire up our cache resolver in the Spring application context in the grails-app/conf/spring/resources.groovy
file and wiring in the dependency on the tenantResolver bean provided by the Multi-Tenant plugin.multiTenantCacheResolver(MultiTenantCacheResolver) {
tenantResolver = ref("tenantResolver")
}
Finally we just need to reference the multiTenantCacheResolver bean on any annotations in parts of the code that need to be multi-tenant aware:@Cacheable(cache = "userCache", cacheResolver = "multiTenantCacheResolver")
def list = {
[users: User.list()] // under the multi-tenant plugin this will only return user instances for the current tenant
}@CacheFlush(caches = "userCache", cacheResolver = "multiTenantCacheResolver")
def save = {
def user = new User(params)
// … save the new user, redirect, handle errors, etc.
}
If you use cache selection when it's not really appropriate it's very easy to get into problems with cache invalidation and stale data.